from OWRpy import * 
import redRGUI 
from OWRpy import * 
import redRGUI 
import redRi18n
_ = redRi18n.get_(package = 'base')

class outliersMulti(OWRpy): 
    globalSettingsList = ['commit']
    def __init__(self, **kwargs):
        
        OWRpy.__init__(self, **kwargs)
        
        #create a R variable in the R session. 
        self.setRvariableNames(["inliers","outliers","mvoutlierSign","mvoutlierOutDF"])

        # declare some variables we will use later. 
        self.RFunctionParam_object = None       #the matrix or dataframe that will be parsed through the sign1 or sign2 function

        # Define the inputs that this widget will accept
        self.inputs.addInput('id0', _('R Variable Object'), signals.base.RDataFrame, self.processobject) #pyhton function(id, name, type, def function)
        
        # Define the outputs of this widget
        self.outputs.addOutput('id0', 'Inliers', signals.base.RDataFrame)
        self.outputs.addOutput('id1', 'Outliers', signals.base.RDataFrame)
        self.outputs.addOutput('id2', 'Data with 0/1 column', signals.base.RDataFrame)
        self.outputs.addOutput('id3', 'x.dist', signals.base.RVector)
        self.outputs.addOutput('id4', 'cutoff', signals.base.RVariable)

        #GUI
        area = redRGUI.base.widgetBox(self.controlArea,orientation='horizontal') 
        inputs = redRGUI.base.widgetBox(area,orientation='vertical')
        options = redRGUI.base.widgetBox(area,orientation='vertical')
        area.layout().setAlignment(options,Qt.AlignTop)

        self.namesList = redRGUI.base.listBox(inputs, label = _('Select at least two elements'))
        self.namesList.setSelectionMode(QAbstractItemView.ExtendedSelection)
        
        self.type = redRGUI.base.radioButtons(options,  label = "Detection based on", 
        buttons = ['Mahalonobis distance (sign1)', 'Principal components (sign2)'],setChecked='Mahalonobis distance (sign1)',
        orientation='vertical',callback=self.changeType) # callback calls function to gray-out sign2 options if sign1 is selected
        
        self.qcrit = redRGUI.base.lineEdit(self.controlArea, label = "Critical quantile value for outlier detection [0 to 1]", text = '0.975')
        self.explvar = redRGUI.base.lineEdit(self.controlArea, label = "Variance covered by robust principal components [0 to 1]", text = '0.99')
        self.explvar.setDisabled(True)
        self.makeplot = redRGUI.base.checkBox(self.controlArea, label = "Plot", buttons = ['Plot diagnostic'], setChecked = 'FALSE')
        
        # Commit
        self.commit = redRGUI.base.commitButton(self.bottomAreaRight, _("Commit"), alignment=Qt.AlignLeft, 
        callback = self.commitFunction, processOnInput=True)
        self.RoutputWindow = redRGUI.base.textEdit(self.controlArea, label = _("RoutputWindow"))


    def processobject(self, data):
        if not self.require_librarys(["mvoutlier"]):
            self.status.setText('R Libraries Not Loaded.')
            return
        if data:
            self.RFunctionParam_object=data.getData()
            self.data = data
            self.namesList.update(self.R('colnames('+self.RFunctionParam_object+')', wantType = 'list'))
            if self.commit.processOnInput():
                self.commitFunction()
        else:
            self.RFunctionParam_object=''


    def commitFunction(self):
        # Data is required. If not received, do nothing.
        if not self.RFunctionParam_object: 
            self.status.setText('Data is missing.')
            return
        if len(self.namesList.selectedItems()) < 2: 
            self.status.setText('Select at least two items, or use widget "Ouliers univariate" for single variable outlier detection.')
            return
        else:
            self.status.setText('Data processing...')
        
        # START COLLECTION THE R PARAMETERS THAT WILL CREATE THE R CODE TO EXECUTE
        self.R('require(mvoutlier)')
        injection = []
        selectedDFItems = []
        for name in self.namesList.selectedItems():
            selectedDFItems.append('"'+unicode(name)+'"') # get the text of the selected items
        
        if self.namesList:
            self.R('objectsSelected<-as.data.frame('+self.RFunctionParam_object+'[colnames('+self.RFunctionParam_object+')'+' %in% c('+','.join(selectedDFItems)+')'+',drop = FALSE])', wantType = 'NoConversion')
            injection.append('objectsSelected')
        if unicode(self.qcrit.text()) != '':
            string = 'qcrit='+unicode(self.qcrit.text())+''
            injection.append(string)     
        if self.makeplot.getChecked():
            string = 'makeplot=TRUE'
            injection.append(string)
        if self.type.getChecked() == 'Mahalonobis distance (sign1)':
            test = 'sign1'
        elif self.type.getChecked() == 'Principal components (sign2)':
            test = 'sign2'
            if unicode(self.explvar.text()) != '':
                string = 'explvar='+unicode(self.explvar.text())+''
                injection.append(string)   

        # combine all the parameters in the a string    
        inj = ','.join(injection)
        
        self.R('%s<-%s(%s)' % (self.Rvariables['mvoutlierSign'],test, inj), wantType = 'NoConversion')
        self.R(self.Rvariables['mvoutlierOutDF']+'<-cbind('+unicode(self.RFunctionParam_object)+','+self.Rvariables['mvoutlierSign']+'$wfinal01)')
        self.R(self.Rvariables['inliers']+'<-'+unicode(self.RFunctionParam_object)+'['+self.Rvariables['mvoutlierSign']+'$wfinal01==1,]')
        self.R(self.Rvariables['outliers']+'<-'+unicode(self.RFunctionParam_object)+'['+self.Rvariables['mvoutlierSign']+'$wfinal01==0,]')

        tmp = self.R('paste("<u>Cut off</u>: ",'+self.Rvariables['mvoutlierSign']+'$const, "\n", "<u>Number of outliers</u>: ", length('+self.Rvariables['mvoutlierSign']+'$wfinal01['+self.Rvariables['mvoutlierSign']+'$wfinal01==0]), " over ", length('+self.Rvariables['mvoutlierSign']+'$wfinal01), collapse ="\n")')
        self.RoutputWindow.clear()
        self.RoutputWindow.insertHtml('<br><pre>'+tmp+'</pre>')
        
        self.rSend('id0', signals.base.RDataFrame(self, data=self.Rvariables['inliers']))
        self.rSend('id1', signals.base.RDataFrame(self, data=self.Rvariables['outliers']))
        self.rSend('id2', signals.base.RDataFrame(self, data=self.Rvariables['mvoutlierOutDF']))
        self.rSend('id3', signals.base.RVector(self, data=self.Rvariables['mvoutlierSign']+'$x.dist'))
        self.rSend('id4', signals.base.RVariable(self, data=self.Rvariables['mvoutlierSign']+'$const'))
        self.status.setText('Data processed and sent.')
        
        #unload (detach) packages, due to potential conflicts between "robCompositions" and "compositions"
        self.R('detach(package:mvoutlier);detach(package:robCompositions)') 
        

    # Based on the user selections some parameters is not valid. This is all documented in the R help for mvoutlier
    # Here we are instructing the GUI to disable those parameters that are invalid. 

    def changeType(self):
        if self.type.getChecked() =='Mahalonobis distance (sign1)':
            self.explvar.setDisabled(True)
        else:    
            self.explvar.setEnabled(True)
